home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Graphics 2D / Snapshot / Snapshot.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-06  |  13.1 KB  |  460 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        Snapshot.c
  3.  
  4.     Contains:    This application demonstrates how to quickly and        
  5.                 efficiently capture the main device's desktop into            
  6.                 a window.  The program basically reads the image             
  7.                 stored in the the main device's pixmap then copies            
  8.                 it to a custom pixmap.  The custom pixmap is de-            
  9.                 fined at the same depth of the main device and                 
  10.                 contains an identical copy of that device's color-            
  11.                 table.  This is done to provide the fastest                 
  12.                 performance possible when copying from an offscreen            
  13.                 to onscreen pixmap.  By making sure the pixel values        
  14.                 map to the exact same colors in both colortables,            
  15.                 copybits will do a direct transfer of bits without            
  16.                 wasting time remapping the colors.  Also the ctSeed            
  17.                 field for each colortable should be the same.  Finally,        
  18.                 since the main device's bounding rect is different            
  19.                 than that of the offscreen's, the copying performance        
  20.                 for the device to the offscreen is slightly affected        
  21.                 because of the scaling required.  However, the copying        
  22.                 performance for the offscreen to the window is the             
  23.                 best possible since the bounding rects for each are            
  24.                 identical.                                        
  25.  
  26.     Written by: EL    
  27.  
  28.     Copyright:    Copyright © 1991-1999 by Apple Computer, Inc., All Rights Reserved.
  29.  
  30.                 You may incorporate this Apple sample source code into your program(s) without
  31.                 restriction. This Apple sample source code has been provided "AS IS" and the
  32.                 responsibility for its operation is yours. You are not permitted to redistribute
  33.                 this Apple sample source code as "Apple sample source code" after having made
  34.                 changes. If you're going to re-distribute the source, we require that you make
  35.                 it clear in the source that the code was descended from Apple sample source
  36.                 code, but that you've made changes.
  37.  
  38.     Change History (most recent first):
  39.                 08/2000        JM                Carbonized, non-Carbon code is commented out
  40.                                             for demonstration purposes.
  41.                 11/6/1999    GGS                 Updated to work with modern (1999) Mac OS.  
  42.                                             Fixed a PixMap disposing bug.  Updated casts
  43.                                             and headers.
  44.                 7/14/1999    KG                Updated for Metrowerks Codewarror Pro 2.1                
  45.  
  46. */
  47.  
  48. #include "CarbonPrefix.h"
  49. #include <Navigation.h>
  50. #include <Script.h>
  51. #include <Files.h>
  52. #include <AppleEvents.h>
  53. #include <Errors.h>
  54. #include <Events.h>
  55. #include <Fonts.h>
  56. #include <Gestalt.h>
  57. #include <Memory.h>
  58. #include <Menus.h>
  59. #include <OSUtils.h>
  60. #include <QDOffscreen.h>
  61. #include <QuickDraw.h>
  62. #include <Resources.h>
  63. #include <Script.h>
  64. #include <ToolUtils.h>
  65. #include <Windows.h>
  66. #include <TextEdit.h>
  67. #include <Dialogs.h>
  68.  
  69. /* Constant Declarations */
  70.  
  71. #define    WWIDTH        ((qd.screenBits.bounds.right - qd.screenBits.bounds.left) / 2)
  72. #define    WHEIGHT        ((qd.screenBits.bounds.bottom - qd.screenBits.bounds.top) / 2)
  73.  
  74. #define WLEFT        (((qd.screenBits.bounds.right - qd.screenBits.bounds.left) - WWIDTH) / 2)
  75. #define WTOP        (((qd.screenBits.bounds.bottom - qd.screenBits.bounds.top) - WHEIGHT) / 2)
  76.  
  77. #define MENU_BAR_ID            128
  78. #define MENU_BAR_IDX         129
  79. #define FILE_MENU             128
  80.  
  81. enum {
  82.     FILE_NEW = 1,
  83.     FILE_REFRESH,
  84.     FILE_CLOSE,
  85.     FILE_SAVE,
  86.     FILE_QUIT
  87. };
  88.  
  89. /* Global Variable Definitions */
  90.  
  91. WindowPtr    gWindow;            /* Main window */
  92. PixMap        gPixMap;            /* Offscreen pixmap */
  93. Rect        gBounds;            /* Offscreen pixmap's bounding rect */
  94. Boolean        gDone = false;        // Application termination global
  95.  
  96. void initMac();
  97. void setUp();
  98. void handleMenuSelection(long result);
  99. void handleKeyPress(EventRecord *);
  100. void saveToPICTFile();
  101. void createWindow();
  102.  
  103. void initPixmap();
  104. void createImage();
  105. void drawImage();
  106.  
  107. pascal OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, unsigned long refIn);
  108.  
  109. void doEventLoop();
  110.  
  111. void main()
  112. {
  113.     initMac();
  114.     
  115.     setUp();
  116.     
  117.     createWindow();                /* Create a window to display the final image. */
  118.     
  119.     initPixmap();                /* Initialize offscreen pixmap. */
  120.     createImage();                /* Copy the main screen's pixmap into the offscreen's. */
  121.     drawImage();                /* Copy the offscreen's pixmap onto the window. */
  122.  
  123.     doEventLoop();                /* Handle any events. */
  124.     
  125.     DisposeWindow( gWindow );
  126. }
  127.  
  128. void initMac()
  129. {
  130.     /*MaxApplZone();
  131.     
  132.     InitGraf( &qd.thePort );
  133.     InitFonts();
  134.     InitWindows();
  135.     InitMenus();
  136.     TEInit();
  137.     InitDialogs( nil );*/
  138.     InitCursor();
  139.     FlushEvents( 0, everyEvent );
  140. }
  141.  
  142. void setUp()
  143. {
  144.     Handle    menuBar;
  145.     OSErr     anErr = noErr;
  146.     long    aLong;
  147.     long    response;
  148.     
  149.     anErr = Gestalt(gestaltSystemVersion, &response);
  150.     
  151.     // Carbon Porting guidelines say provide alternate menu bar/menu scheme for OS X
  152.     // This is just one way of doing this, which is pretty static
  153.     if (response >= 0x01000) 
  154.         menuBar = GetNewMBar(MENU_BAR_IDX);
  155.     else
  156.         menuBar = GetNewMBar(MENU_BAR_ID);
  157.     
  158.     if ( menuBar == nil || anErr != noErr )
  159.          ExitToShell();    
  160.  
  161.     SetMenuBar(menuBar);
  162.     DisposeHandle(menuBar);
  163.  
  164.     DrawMenuBar();
  165.     
  166.     // Install 'quit' event handler
  167.     if ((Gestalt(gestaltAppleEventsAttr, &aLong) == noErr)) {
  168.             anErr = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
  169.                      NewAEEventHandlerUPP(AEQuitHandler), 0, false);
  170.             if (anErr != noErr)  
  171.                 ExitToShell();
  172.     }
  173. }
  174.  
  175. void createWindow()
  176. {
  177.     Rect     wBounds;
  178.     BitMap    bitMap;
  179.     Rect     tempRect1;
  180.     int        top, left, width, height;
  181.     
  182.     GetQDGlobalsScreenBits(&bitMap);
  183.     
  184.     width = ((bitMap.bounds.right - bitMap.bounds.left) / 2);
  185.     height = ((bitMap.bounds.bottom - bitMap.bounds.top) / 2);
  186.  
  187.     left = (((bitMap.bounds.right - bitMap.bounds.left) - width) / 2);
  188.     top = (((bitMap.bounds.bottom - bitMap.bounds.top) - height) / 2);
  189.     
  190.     /* Create a window to display the final offscreen image. */
  191.     
  192.     //SetRect( &wBounds, WLEFT, WTOP, WLEFT + WWIDTH, WTOP + WHEIGHT );
  193.     SetRect( &wBounds, left, top, left + width, top + height );
  194.     
  195.     gWindow = NewCWindow( 0L, &wBounds, "\pSnapshot Test", false, documentProc,
  196.                             (WindowPtr)-1L, true, 0L );
  197.                             
  198.     //SetRect( &gBounds, 0, 0, gWindow->portRect.right - gWindow->portRect.left,
  199.     //                gWindow->portRect.bottom - gWindow->portRect.top );
  200.     GetPortBounds(GetWindowPort(gWindow), &tempRect1);
  201.     SetRect( &gBounds, 0, 0, tempRect1.right - tempRect1.left, 
  202.      tempRect1.bottom - tempRect1.top);
  203.                             
  204.     //SetPort( gWindow );
  205.     SetPortWindowPort( gWindow );
  206. }
  207.  
  208. void initPixmap()
  209. {
  210.     Ptr            offBaseAddr;    /* Pointer to the off-screen pixel image */
  211.     short        bytesPerRow;
  212.     GDHandle    mainDevice;
  213.     CTabHandle    cTable;
  214.     short        depth;
  215.  
  216.     /* Get a handle to the main device. */
  217.     mainDevice = GetMainDevice();
  218.  
  219.     /* Store its current pixel depth. */
  220.     depth = (**(**mainDevice).gdPMap).pixelSize;
  221.  
  222.     /* Make an identical copy of its pixmap's colortable. */
  223.     cTable = (**(**mainDevice).gdPMap).pmTable;
  224.     HandToHand( &(Handle)cTable );
  225.  
  226.     bytesPerRow = ((gBounds.right - gBounds.left) * depth) / 8;
  227.     offBaseAddr = NewPtr((unsigned long)bytesPerRow * (gBounds.bottom - gBounds.top));
  228.     
  229.     gPixMap.baseAddr = offBaseAddr;              /* Point to image */
  230.     gPixMap.rowBytes = bytesPerRow | 0x8000;    /* MSB set for PixMap */
  231.     gPixMap.bounds = gBounds;                     /* Use given bounds */
  232.     gPixMap.pmVersion = 0;                       /* No special stuff */
  233.     gPixMap.packType = 0;                        /* Default PICT pack */
  234.     gPixMap.packSize = 0;                        /* Always zero in mem */
  235.     gPixMap.hRes = 72;                          /* 72 DPI default res */
  236.     gPixMap.vRes = 72;                          /* 72 DPI default res */
  237.     gPixMap.pixelSize = depth;                   /* Set # bits/pixel */
  238.     //gPixMap.planeBytes = 0;                      /* Not used */
  239.     //gPixMap.pmReserved = 0;                      /* Not used */
  240.  
  241.     gPixMap.pixelType = 0;                       /* Indicates indexed */
  242.     gPixMap.cmpCount = 1;                        /* Have 1 component */
  243.     gPixMap.cmpSize = depth;                     /* Component size=depth */
  244.     gPixMap.pmTable = cTable;                     /* Handle to CLUT */
  245. }
  246.  
  247. void createImage()
  248. {
  249.     GDHandle    mainDevice;
  250.  
  251.     mainDevice = GetMainDevice();
  252.  
  253.     /* Store the screen's pixmap image in the offscreen pixmap. */
  254.  
  255.     CopyBits( (BitMap *)*(**mainDevice).gdPMap, (BitMap *)(&gPixMap),
  256.                 &(**(**mainDevice).gdPMap).bounds, &gPixMap.bounds, srcCopy, 0l );
  257. }
  258.  
  259. void drawImage(WindowPtr theWindow)
  260. {
  261.     Rect tempRect1;
  262.     /* Copy the offscreen image back onto the window. */
  263.  
  264.     //CopyBits( (BitMap *)&gPixMap, &gWindow->portBits, &gPixMap.bounds,
  265.     //            &gWindow->portRect, srcCopy, 0l);
  266.     
  267.     CopyBits( (BitMap *)&gPixMap, GetPortBitMapForCopyBits(GetWindowPort(theWindow)), &gPixMap.bounds,
  268.         GetPortBounds(GetWindowPort(gWindow), &tempRect1), srcCopy, 0L);
  269.                 
  270.     ShowWindow( gWindow );
  271. }
  272.  
  273. void saveToPICTFile()
  274. {
  275.  
  276. /*
  277. Saving a PixMap as a PICT file isn't too hard.
  278.  
  279. 1.  Open a Picture with the port set to the destination of #2.
  280. 2.  CopyBits the PixMap onto itself or another port.  (Because CopyBits is
  281. recorded in Pictures.
  282. 3.  Close the picture.
  283. 4.  Open the data fork for the file.
  284. 5.  Write out 512 bytes of zeros followed by the contents of the Picture
  285. handle.
  286. 6.  Close the file.
  287. */
  288.  
  289.     PicHandle            picHandle;
  290.     OSErr                anErr = noErr;
  291.     OSType              fileTypeToSave = 'PICT';
  292.     OSType              creatorType = 'ogle';
  293.     NavReplyRecord      reply;
  294.     NavDialogOptions    dialogOptions;
  295.     FSSpec              documentFSSpec;
  296.     long                inOutCount;
  297.     short                refNum, count;
  298.     AEKeyword           theKeyword;
  299.     DescType            actualType;
  300.     unsigned char         header[512];
  301.     Size                actualSize;
  302.     Rect                tempRect1;
  303.     
  304.     CopyBits(GetPortBitMapForCopyBits(GetWindowPort(FrontWindow())), (BitMap*) &gPixMap, 
  305.      GetPortBounds(GetWindowPort(gWindow), &tempRect1), &gPixMap.bounds, srcCopy, 0L);
  306.     
  307.     SetPortWindowPort(gWindow);
  308.     
  309.     picHandle = OpenPicture(&gPixMap.bounds);
  310.     
  311.     CopyBits((BitMap*) &gPixMap, GetPortBitMapForCopyBits(GetWindowPort(FrontWindow())), &gPixMap.bounds, 
  312.      GetPortBounds(GetWindowPort(gWindow), &tempRect1), srcCopy, 0L);
  313.      
  314.     ClosePicture();
  315.  
  316.     for (count = 0; count < 512; count++)
  317.         header[count] = 0x00;
  318.  
  319.     anErr = NavGetDefaultDialogOptions(&dialogOptions); 
  320.     dialogOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
  321.     
  322.     anErr = NavPutFile( nil, 
  323.                         &reply, 
  324.                         &dialogOptions, 
  325.                         nil,
  326.                         fileTypeToSave, 
  327.                         creatorType, 
  328.                         nil );
  329.  
  330.     if (anErr == noErr && reply.validRecord) {
  331.         anErr = AEGetNthPtr(&(reply.selection), 1, typeFSS,
  332.                                 &theKeyword, &actualType,
  333.                                 &documentFSSpec, sizeof(documentFSSpec),
  334.                                 &actualSize );
  335.     if (anErr == noErr) {
  336.         
  337.                 anErr = FSpCreate(&documentFSSpec, creatorType, fileTypeToSave, smSystemScript);
  338.             if (anErr == dupFNErr) {
  339.                 anErr = FSpDelete(&documentFSSpec);
  340.                 anErr = FSpCreate(&documentFSSpec, creatorType, fileTypeToSave, smSystemScript);
  341.             }        // this is quick 'n' dirty or there'd be more robust handling here
  342.             
  343.             // write the file
  344.             FSpOpenDF(&documentFSSpec, fsRdWrPerm, &refNum );
  345.             inOutCount = 512;
  346.                anErr = FSWrite(refNum, &inOutCount, header);        // write the header
  347.             if (anErr == noErr) {
  348.                 inOutCount = GetHandleSize((Handle)picHandle);
  349.                 anErr = FSWrite(refNum,&inOutCount,*picHandle);
  350.             }
  351.             FSClose( refNum );
  352.         }
  353.         reply.translationNeeded = false;
  354.         anErr = NavCompleteSave(&reply, kNavTranslateInPlace);
  355.     
  356.        NavDisposeReply(&reply);
  357.     }
  358.     
  359.     KillPicture(picHandle);
  360. }
  361.  
  362. void handleMenuSelection(long result)
  363. {
  364.     int menuID, menuItem;
  365.     RgnHandle rgnHandle = NewRgn();
  366.     
  367.     menuID = HiWord(result);
  368.     menuItem = LoWord(result);
  369.     
  370.     if (menuID == FILE_MENU) {
  371.         if (menuItem == FILE_SAVE)
  372.             saveToPICTFile();
  373.         else if (menuItem == FILE_QUIT)
  374.             gDone = true;
  375.         else if (menuItem == FILE_CLOSE) {
  376.             DisposeWindow(FrontWindow());
  377.             if (FrontWindow() == NULL)
  378.                 gDone = true;
  379.         }
  380.         else if (menuItem == FILE_NEW) {
  381.         
  382.         }
  383.         else if (menuItem == FILE_REFRESH) {
  384.             createImage();
  385.             drawImage(FrontWindow());
  386.             QDFlushPortBuffer(GetWindowPort(FrontWindow()), GetPortVisibleRegion(GetWindowPort(FrontWindow()), rgnHandle));
  387.         }
  388.     }
  389.     HiliteMenu(0);
  390.     DisposeRgn(rgnHandle);
  391. }
  392.  
  393. void handleKeyPress(EventRecord *event)
  394. {
  395.     char    key;
  396.  
  397.     key = event->message & charCodeMask;
  398.     
  399.     // just check to see if we want to quit...
  400.     
  401.     if ( event->modifiers & cmdKey )        /* Command key down? */
  402.         handleMenuSelection(MenuKey(key));
  403. }
  404.  
  405. // Apple Event - "Quit"
  406. pascal OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, unsigned long refIn)
  407. {
  408.     #pragma unused (messagein,refIn,reply)
  409.     
  410.     gDone = true;
  411.     return noErr;
  412. }
  413.  
  414. void doEventLoop()
  415. {
  416.     EventRecord anEvent;
  417.     WindowPtr   evtWind;
  418.     short       clickArea;
  419.     Rect        screenRect;
  420.  
  421.     while (!gDone)
  422.     {
  423.         if (WaitNextEvent( everyEvent, &anEvent, 0, nil ))
  424.         {
  425.             if (anEvent.what == mouseDown)
  426.             {
  427.                 clickArea = FindWindow( anEvent.where, &evtWind );
  428.                 
  429.                 if (clickArea == inMenuBar)
  430.                     handleMenuSelection(MenuSelect(anEvent.where));
  431.                 else if (clickArea == inDrag)
  432.                 {
  433.                     //screenRect = (**GetGrayRgn ()).rgnBBox;
  434.                     GetRegionBounds(GetGrayRgn(), &screenRect);
  435.                     DragWindow( evtWind, anEvent.where, &screenRect );
  436.                 }
  437.                 else if (clickArea == inContent)
  438.                 {
  439.                     if (evtWind != FrontWindow())
  440.                         SelectWindow( evtWind );
  441.                 }
  442.                 else if (clickArea == inGoAway)
  443.                     if (TrackGoAway( evtWind, anEvent.where ))
  444.                         gDone = true;
  445.             }
  446.             else if (anEvent.what == updateEvt)
  447.             {
  448.                 evtWind = (WindowPtr)anEvent.message;    
  449.                 //SetPort( evtWind );
  450.                 SetPortWindowPort( evtWind );
  451.                 
  452.                 BeginUpdate( evtWind );
  453.                 drawImage( evtWind );
  454.                 EndUpdate (evtWind);
  455.             }
  456.             else if (anEvent.what == autoKey || anEvent.what == keyDown)
  457.                 handleKeyPress(&anEvent);
  458.         }
  459.     }
  460. }